"""Multi-dynamic form field.

This is the core LDAP-friendly input field.  It allows you to dynamically add
more values by hitting a little plus symbol.
"""

from django.forms import Field, Widget, ValidationError
from django.forms.fields import EMPTY_VALUES
from django.forms.util import ErrorList
from django.utils.datastructures import MultiValueDict, MergeDict
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _

class MultiDynamicField(Field):

    default_error_messages = {
        'invalid': _(u'Enter a list of values.'),
    }

    def __init__(self, field, widget=None, *args, **kwargs):
        field.required = False
        self.field = field
        if widget is None:
            widget = MultiDynamicWidget(field.widget)
        elif isinstance(widget, type) and issubclass(widget, MultiDynamicWidget):
            widget = widget(field.widget)
        elif not isinstance(widget, MultiDynamicWidget):
            widget = MultiDynamicWidget(widget)
        super(MultiDynamicField, self).__init__(widget=widget, *args, **kwargs)

    def clean(self, value):
        clean_data = []
        errors = ErrorList()
        if not value or isinstance(value, (list, tuple)):
            if not value or not [v for v in value if v not in EMPTY_VALUES]:
                if self.required:
                    raise ValidationError(self.error_messages['required'])
                else:
                    return []
        else:
            raise ValidationError(self.error_messages['invalid'])

        for index, field_value in enumerate(value):
            if field_value:
                try:
                    clean_data.append(self.field.clean(field_value))
                except ValidationError, e:
                    errors.extend(e.messages)
        if errors:
            # XXX: Having multiple errors may be annoying.
            raise ValidationError(errors)

        if not clean_data and self.required:
            raise ValidationError(self.error_messages['required'])

        return clean_data


class MultiDynamicWidget(Widget):

    def __init__(self, widget, *args, **kwargs):
        super(MultiDynamicWidget, self).__init__(*args, **kwargs)
        self.widget = widget

    def render(self, name, value, attrs=None):
        if not isinstance(value, list):
            value = [value]
        output = []
        final_attrs = self.build_attrs(attrs)
        input_id = final_attrs.get('id', None)
        # XXX Filter empty values?
        if not value:
            value = [None]
        for index, field_value in enumerate(value):
            if input_id:
                final_attrs = dict(final_attrs, id='%s__%s' % (input_id, index))
            output.append(self.widget.render(
                name,
                field_value,
                final_attrs
            ))
        return mark_safe(self.format_output(output, name))

    def format_output(self, rendered_widgets, name):
        return u''.join(rendered_widgets)

    @classmethod
    def id_for_label(self, id_):
        if id_:
            id_ += '__0'
        return id_

    def value_from_datadict(self, data, files, name):
        if isinstance(data, (MultiValueDict, MergeDict)):
            return data.getlist(name)
        return data.get(name, None)
